﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Objects;
using System.Linq;
using Extensions;
using Natural.Model;
using Natural.Providers;

namespace Natural.Managers
{    
    public class DataEntityContext : ObjectContext
    {
        public DataEntityContext(string modelName):base("Name=" + modelName)           
        {
            DefaultContainerName = modelName;
            ContextOptions.LazyLoadingEnabled = true;
            ContextOptions.ProxyCreationEnabled = false;
        }

        public void Update<T>(T entity) where T : class
        {
            ObjectSet<T> objectSet = GetObjectSet<T>();
            int? id = PkValue<T>(objectSet, entity);
            if (id.GetValueOrDefault(0) > 0)
            {
                objectSet.Attach(entity);
            }
            else
            {
                objectSet.AddObject(entity);
            }
            this.SaveChanges();
        }
        public void Delete<T>(T entity) where T : class
        {
            ObjectSet<T> objectSet = GetObjectSet<T>();
            int? id = PkValue<T>(objectSet, entity);
            if (id.GetValueOrDefault(0) > 0)
            {
                objectSet.DeleteObject(entity);
                this.SaveChanges();
            }
            
        }

        private ObjectSet<T> GetObjectSet<T>() where T : class
        {
            return CreateObjectSet<T>(DefaultContainerName + "." + typeof(T).Name);
        }
        private int? PkValue<T>(ObjectSet<T> objectSet, T entity) where T : class
        {
            string keyName = objectSet.EntitySet.ElementType.KeyMembers.Select(k => k.Name).FirstOrDefault();
            Type type = typeof(T);
            return (int?)type.GetProperty(keyName).GetValue(entity, null);
        }
    }

    public abstract  class DataEntityFramework : IData
    {
        public string ModelName = null;

        
        private DataEntityContext GetContext()
        {
            return new DataEntityContext(ModelName);
        }
        public virtual T[] Get<T>(Expression expression) 
        {
            string sql = string.Format("SELECT VALUE this FROM {0} AS this ", typeof(T).Name);
            if (!string.IsNullOrEmpty(expression.Text)) sql = sql + " WHERE " + expression.Text; 

            T[] result = null;
            ObjectParameter[] _parameters = null;
            if (expression.Parameters != null) _parameters = expression.Parameters.Select(p => new ObjectParameter(p.Key, p.Value)).ToArray();
            using (DataEntityContext context = GetContext())
            {
                ObjectQuery<T> query = context.CreateQuery<T>(sql, _parameters);
                if (!string.IsNullOrEmpty(expression.Includes)) expression.Includes.Split(',').Foreach(p => query = query.Include(p));
                result = (from i in query select i).ToArray();
                //result.Each(p => context.Detach(p));                
            }
            return result;
        }
        public virtual void Update<T>(T entity, string includes) where T : class
        {
            using (DataEntityContext context = GetContext())
            {
                context.Update<T>(entity);
            }
        }
        public virtual void Delete<T>(T entity, string includes) where T : class
        {
            using (DataEntityContext context = GetContext())
            {
                context.Delete<T>(entity);
            }
        }
        public virtual List<object[]> Query(Expression expression) 
        {
            List<object[]> rows = new List<object[]>();
            ObjectParameter[] _parameters = null;
            if (expression.Parameters != null) _parameters = expression.Parameters.Select(p => new ObjectParameter(p.Key, p.Value)).ToArray();
            using (DataEntityContext context = GetContext())
            {
                ObjectQuery<DbDataRecord> query = context.CreateQuery<DbDataRecord>(expression.Text, _parameters);
                foreach (DbDataRecord record in query)
                {
                    object[] row = new object[record.FieldCount];
                    for(int i = 0; i< record.FieldCount;i++)
                    {
                       row[i]=record.GetValue(i);
                    }
                    rows.Add(row);
                }            
            }
            return rows;
        }
    }
}
